package org.xukai.common.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author xukai
 * @desc
 * @date 2017-03-21-14:30
 */


public class ExpiryMap<K, V> {
    private final Map<K, ExpiryValue<V>> map;
    private Thread monitor;
    private long checkInterval;
    private volatile boolean start;
    private Object lock;
    private ExpiryMap.ExpiriedCallback<K, V> expiriedCallback;

    public long getCheckInterval() {
        return this.checkInterval;
    }

    public void setCheckInterval(long checkInterval) {
        this.checkInterval = checkInterval;
    }

    public ExpiryMap() {
        this(new HashMap());
    }

    public ExpiryMap(int initialCapacity) {
        this(new HashMap(initialCapacity));
    }

    public ExpiryMap(Map<K, ExpiryMap.ExpiryValue<V>> map) {
        this.checkInterval = 1000L;
        this.lock = new Object();
        this.map = map;
    }

    public Map<K, ExpiryMap.ExpiryValue<V>> getMap() {
        return this.map;
    }

    public boolean containsKey(K key) {
        return this.map.containsKey(key);
    }

    public void put(K key, long expiry, V value) {
        this.map.put(key, ExpiryMap.ExpiryValue.build(expiry, value));
    }

    public void put(K key, Date created, long expiry, V value) {
        this.map.put(key, ExpiryMap.ExpiryValue.build(created, expiry, value));
    }

    public ExpiryMap.ExpiryValue<V> remove(K key) {
        return (ExpiryMap.ExpiryValue)this.map.remove(key);
    }

    public V getValue(K key) {
        ExpiryMap.ExpiryValue<V> value = (ExpiryMap.ExpiryValue)this.map.get(key);
        return null == value?null:value.getValue();
    }

    public ExpiryMap.ExpiryValue<V> get(K key) {
        return (ExpiryMap.ExpiryValue)this.map.get(key);
    }

    public void startup() {
        if(!this.start) {
            if(this.checkInterval < 100L) {
                throw new IllegalArgumentException("checkInterval has to be more than or equal 100 ms");
            } else {
                this.start = true;
                this.monitor = new Thread(new Runnable() {
                    public void run() {
                        for(; ExpiryMap.this.start; ExpiryMap.this.checkExpiry()) {
                            synchronized(ExpiryMap.this.lock) {
                                try {
                                    ExpiryMap.this.lock.wait(ExpiryMap.this.checkInterval);
                                } catch (InterruptedException var4) {
                                    var4.printStackTrace();
                                }
                            }
                        }

                    }
                });
                this.monitor.start();
            }
        }
    }

    protected void checkExpiry() {
        Set keys = this.map.keySet();
        ArrayList delete = new ArrayList();
        Iterator i$ = keys.iterator();

        K key;
        ExpiryMap.ExpiryValue<V> value;
        while(i$.hasNext()) {
            key = (K)i$.next();
            value = (ExpiryMap.ExpiryValue)this.map.get(key);
            if(this.isExpiried(value)) {
                delete.add(key);
            }
        }

        i$ = delete.iterator();

        while(i$.hasNext()) {
            key = (K)i$.next();
            value = (ExpiryMap.ExpiryValue)this.map.remove(key);
            if(null != this.expiriedCallback) {
                try {
                    this.expiriedCallback.onExpiried(key, value == null?null:value.getValue());
                } catch (Exception var7) {
                    var7.printStackTrace();
                }
            }
        }

    }

    protected boolean isExpiried(ExpiryMap.ExpiryValue<V> value) {
        if(value != null && value.getCreated() != null && value.getExpiry() > 0L) {
            Date expiryDate = new Date(value.getCreated().getTime() + value.getExpiry() * 1000L);
            return (new Date()).after(expiryDate);
        } else {
            return true;
        }
    }

    public void shutdown() {
        if(this.start) {
            this.start = false;
            Object var1 = this.lock;
            synchronized(this.lock) {
                this.lock.notifyAll();
            }
        }
    }

    public ExpiryMap.ExpiriedCallback<K, V> getExpiriedCallback() {
        return this.expiriedCallback;
    }

    public void setExpiriedCallback(ExpiryMap.ExpiriedCallback<K, V> expiriedCallback) {
        this.expiriedCallback = expiriedCallback;
    }

    public String toString() {
        return this.map.toString();
    }

    public interface ExpiriedCallback<K, V> {
        void onExpiried(K var1, V var2);
    }

    public static class ExpiryValue<V> implements Serializable {
        private static final long serialVersionUID = 8422716038591883058L;
        private Date created;
        private long expiry;
        private V value;

        public ExpiryValue() {
        }

        public Date getCreated() {
            return this.created;
        }

        public void setCreated(Date created) {
            this.created = created;
        }

        public long getExpiry() {
            return this.expiry;
        }

        public void setExpiry(long expiry) {
            this.expiry = expiry;
        }

        public V getValue() {
            return this.value;
        }

        public void setValue(V value) {
            this.value = value;
        }

        public static <V> ExpiryMap.ExpiryValue<V> build(long expiry, V value) {
            return build(new Date(), expiry, value);
        }

        public static <V> ExpiryMap.ExpiryValue<V> build(Date created, long expiry, V value) {
            ExpiryMap.ExpiryValue ev = new ExpiryMap.ExpiryValue();
            ev.setCreated(created);
            ev.setExpiry(expiry);
            ev.setValue(value);
            return ev;
        }

        public String toString() {
            return "{created:" + this.created + ",expiry:" + this.expiry + ",value:" + this.value + "}";
        }
    }
}


